import sys
import json
import requests
import time  # (用于模拟耗时操作)
import os  # (用于处理文件路径)
import zipfile  # (用于解压)
import pandas as pd  # (用于读写 Excel)

from requests.exceptions import JSONDecodeError
from PyQt5.QtWidgets import QWidget, QApplication, QFileDialog, QMessageBox
# 导入 QThread, pyqtSignal, QObject, pyqtSlot
from PyQt5.QtCore import QDate, QSettings, QEvent, QDir, QThread, pyqtSignal, QObject, pyqtSlot

# 假设你的 ui 文件在 'qunar' 文件夹下
# 如果 qunar_ui.py 和这个 main.py 在同一个文件夹，请改成:
# from qunar_ui import qunar_ui
from qunar.qunar_ui import qunar_ui


# --- 步骤 1: 重新设计 Worker 类 ---

class ExportWorker(QObject):
    """
    负责执行完整导出流程(1-7步)的工作类
    """
    # 定义信号：
    # signal_progress: 报告当前进展 (e.g., "正在下载文件...")
    signal_progress = pyqtSignal(str)
    # signal_finished: 报告整个流程成功完成
    signal_finished = pyqtSignal(str)
    # signal_failure: 报告流程中任意一步的失败
    signal_failure = pyqtSignal(str)

    # 存储从主线程传来的参数
    def __init__(self):
        super().__init__()
        self.session = ""
        self.start_date = ""
        self.end_date = ""
        self.save_path = ""  # 这是保存文件夹

    @pyqtSlot(str, str, str, str)
    def run_export_flow(self, session, start_date, end_date, save_path):
        """
        执行完整的(1-7步)导出流程
        """
        print("[Worker 线程] 开始执行完整导出流程...")
        self.session = session
        self.start_date = start_date
        self.end_date = end_date
        self.save_path = save_path

        try:
            # --- 步骤 1: 查询订单总数 ---
            self.signal_progress.emit("步骤 1/7: 查询订单总数...")
            total_count = self.task_1_query_count()
            self.signal_progress.emit(f"步骤 1/7: 查询完毕，总订单数: {total_count}")

            if total_count == 0:
                self.signal_finished.emit("流程完成：订单总数为 0，无需导出。")
                return

            # --- 步骤 2: 导出订单 (生成文件) ---
            self.signal_progress.emit("步骤 2/7: 正在请求导出订单...")
            self.task_2_request_export(total_count)
            self.signal_progress.emit(f"步骤 2/7: 导出任务已提交")

            self.signal_progress.emit("步骤 2/7: 订单导出成功！")

            # --- 步骤 3: 下载当前导出的订单 ---
            self.signal_progress.emit("步骤 3/7: 正在下载导出的文件...")
            # 目标文件路径
            zip_file_path = os.path.join(self.save_path, "exported_orders.zip")
            self.task_3_download_file(zip_file_path)
            self.signal_progress.emit(f"步骤 3/7: 文件下载完成: {zip_file_path}")

            # --- 步骤 4: 下载的订单解压 ---
            self.signal_progress.emit("步骤 4/7: 正在解压文件...")
            # (模拟) 假设解压后得到 "orders.xlsx"
            excel_file_path = self.task_4_unzip_file(zip_file_path)
            self.signal_progress.emit(f"步骤 4/7: 解压完成: {excel_file_path}")
            excel_file_path = "E:/360MoveData/Users/Administrator/Desktop/order-20251102172110-7c8deb0d-b0cf-4e5f-ba89-b5f82fd05efc.xls"
            # --- 步骤 5: 读取表格获取订单号 ---
            self.signal_progress.emit("步骤 5/7: 正在读取表格...")
            # (模拟) 假设订单号在 "OrderID" 列
            df = self.task_5_read_excel(excel_file_path)
            order_column_name = '订单号'

            if order_column_name not in df.columns:
                raise ValueError(f"Excel 中未找到 '{order_column_name}' 列")

            order_ids = df[order_column_name].tolist()

            self.signal_progress.emit(f"步骤 5/7: 读取完毕，共 {len(order_ids)} 个订单需要获取联系人。")

            # --- (!! 步骤 6: 循环获取联系人 !!) ---
            self.signal_progress.emit("步骤 6/7: 开始获取联系人...")
            contacts = []
            total = len(order_ids)
            for i, order_id in enumerate(order_ids):
                # 2. 将每个订单号 (order_id) 传给 task_6
                contact_name = self.task_6_get_contact(order_id)

                contacts.append(contact_name)
                self.signal_progress.emit(f"步骤 6/7: 正在获取... ({i + 1}/{total})")


            self.signal_progress.emit("步骤 6/7: 所有联系人获取完毕。")

            # --- (!! 核心修改: 步骤 7) ---
            self.signal_progress.emit("步骤 7/7: 正在回写表格...")

            # (!!修改!!) task_7_save_to_excel 接收 df (之前已是如此, 逻辑不变)
            final_excel_path = self.task_7_save_to_excel(df, contacts, excel_file_path)
            self.signal_progress.emit(f"步骤 7/7: 全部完成！最终文件已保存。")

            self.signal_finished.emit(f"导出成功！最终文件保存在:\n{final_excel_path}")

        except JSONDecodeError:
            print(f"[Worker 线程] JSON 解析失败！Session 可能已过期。")
            self.signal_failure.emit("流程失败：Session 已过期，请重新获取。")

        except requests.exceptions.RequestException as e:
            print(f"[Worker 线程] 网络请求失败: {e}")
            self.signal_failure.emit(f"流程失败：网络请求失败: {str(e)}")

        except Exception as e:
            # 捕获所有其他异常 (例如文件没找到, pandas读取失败等)
            print(f"[Worker 线程] 发生未知错误: {e}")
            self.signal_failure.emit(f"流程失败：发生未知错误: {str(e)}")

    # -----------------------------------------------------------------
    # --- 下面是模拟的7个任务步骤 ---
    # --- 你需要用真实的 requests 和文件处理逻辑替换它们 ---
    # -----------------------------------------------------------------

    def task_1_query_count(self):
        print("[Worker 模拟] 1. 正在查询总数...")
        url = "https://tts2.piao.qunar.com/order/queryOrderList"
        payload = {
            "dateType": "canUseDate",
            "startDate": self.start_date,
            "endDate": self.end_date,
            "pageNo": 1,
            "perPageSize": 20
        }
        cookies = {
            "_s": self.session
        }
        response = requests.post(url=url, data=payload, cookies=cookies)
        json = response.json()
        order_total = json.get("data")[0].get("totalRows")
        return order_total

    def task_2_request_export(self, order_total):
        print("[Worker 模拟] 2. 正在请求导出...")
        url = "https://tts2.piao.qunar.com/order/exportOrder"
        payload = {
            "dateType": "canUseDate",
            "startDate": self.start_date,
            "endDate": self.end_date,
            "orderCount": order_total
        }
        cookies = {
            "_s": self.session
        }
        response = requests.post(url=url, data=payload, cookies=cookies)
        response_json = response.json()
        return response_json.get("ret")


    def task_3_download_file(self, save_to_path):
        # 你的 "下载文件" (可能用 requests.get) 代码放这里
        print(f"[Worker 模拟] 3. 正在下载文件...")
        polling_url = "https://tts2.piao.qunar.com/order/queryExportOrderList"
        download_url = "https://tts2.piao.qunar.com/order/downloadOrderReport?reportId="
        cookies = {
            "_s": self.session
        }
        # 1. 构造我们要查找的目标 "orderTime" 字符串
        target_order_time = f"游玩时间:{self.start_date}至{self.end_date}"
        print(f"[Worker 真实] 3. 目标任务: {target_order_time}")

        # 2. 轮询循环 (最多5分钟)
        for attempt in range(5):
            self.signal_progress.emit(f"步骤 3/7: 查询状态 (第 {attempt + 1}/5 次)...")

            response = requests.post(polling_url, cookies=cookies, timeout=10)
            data = response.json()  # 如果session失效，这里会抛出 JSONDecodeError

            if not data.get('ret'):
                raise Exception(f"API返回错误: {data.get('errmsg', '未知错误')}")

            # 任务列表在 data[0]
            task_list = data.get('data', [[]])[0]
            if not task_list:
                self.signal_progress.emit("步骤 3/7: 未拉取到任务列表...")

            found_task = None
            for task in task_list:
                if task.get('orderTime') == target_order_time:
                    # 找到了日期匹配的任务
                    found_task = task
                    break

            if found_task:
                # 找到了任务, 检查状态
                status = found_task.get('exportStatus')
                if status == "已生成":
                    self.signal_progress.emit("步骤 3/7: 状态已生成，准备下载...")
                    task_id = found_task.get('id')
                    if not task_id:
                        raise Exception("任务 '已生成' 但未找到 ID")
                    download_url += str(task_id)
                    # --- 状态正确，ID拿到，执行下载 ---
                    self._task_3_actual_download(download_url, task_id, save_to_path)

                    # 下载成功, 退出 task_3 函数
                    return

                else:
                    # 状态不是 "已生成" (例如 "正在生成")
                    self.signal_progress.emit(f"步骤 3/7: 状态: {status}. 1分钟后重试...")

            else:
                # 任务列表里没有我们的任务
                self.signal_progress.emit("步骤 3/7: 未找到匹配的任务. 1分钟后重试...")

            # (!!重要!!) 等待1分钟 (60秒)
            # (最后一次循环 4 < 4 是 false，不会等待)
            if attempt < 4:
                time.sleep(60)

                # 3. 如果 5 次循环都结束了还没有 return，说明超时了
        raise Exception("步骤 3/7 失败: 导出任务超时，5分钟后状态仍未 '已生成'。")

    def _task_3_actual_download(self, download_url, task_id, save_to_path):
        """
        辅助函数，用于执行真正的文件下载
        (这个下载逻辑是必须的)
        """
        print(f"[Worker 真实] 3. 正在下载 ID: {task_id} 到 {save_to_path}")
        self.signal_progress.emit("步骤 3/7: 正在下载文件...")

        cookies = {"_s": self.session}
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
            "Referer": "https://tts2.piao.qunar.com/"  # (假设的Referer)
        }

        # (!! 猜测 !!) 下载请求可能需要 POST 并附带任务ID

        # 使用 stream=True 来下载大文件
        with requests.get(download_url, cookies=cookies, stream=True, timeout=60) as r:

            # 检查服务器是否返回了错误 (例如JSON) 而不是文件
            content_type = r.headers.get('Content-Type', '')
            if 'application/json' in content_type:
                error_data = r.json()
                raise Exception(f"下载失败: {error_data.get('errmsg', '服务器返回JSON错误')}")

            # 检查 HTTP 错误
            r.raise_for_status()

            # 以二进制块的形式写入文件
            with open(save_to_path, 'wb') as f:
                for chunk in r.iter_content(chunk_size=8192):
                    f.write(chunk)

        print(f"[Worker 真实] 3. 文件下载完成: {save_to_path}")

    def task_4_unzip_file(self, zip_file_path):
        print(f"[Worker 真实] 4. 正在解压 {zip_file_path}...")
        extract_dir = self.save_path

        excel_filename = None

        try:
            with zipfile.ZipFile(zip_file_path, 'r') as zf:
                # 1. (!!修改!!) 查找 ZIP 包内的 Excel 文件
                file_list = zf.namelist()

                # 查找所有 .xls 或 .xlsx 文件
                excel_files = [f for f in file_list if f.endswith('.xls') or f.endswith('.xlsx')]

                if not excel_files:
                    # 2. 如果没找到
                    raise FileNotFoundError("压缩包中未找到任何 .xls 或 .xlsx 文件。")

                if len(excel_files) > 1:
                    # 3. 如果找到多个
                    print(f"[Worker 警告] 4. 压缩包中找到多个 Excel 文件: {excel_files}。将使用第一个: {excel_files[0]}")

                # 4. (!!修改!!) 确定要解压的动态文件名
                excel_filename = excel_files[0]

                print(f"[Worker 真实] 4. 找到目标文件: {excel_filename}")

                # 5. 解压
                zf.extractall(extract_dir)

        except zipfile.BadZipFile:
            raise Exception(f"下载的文件不是有效的 ZIP 压缩包。")
        except Exception as e:
            # (例如磁盘已满)
            print(f"[Worker 错误] 4. 解压时发生错误: {e}")
            raise  # 重新抛出原始异常

        # 6. (!!修改!!) 构造动态的、正确的完整路径
        excel_path = os.path.join(extract_dir, excel_filename)

        # 7. 检查解压后的文件是否存在
        if not os.path.exists(excel_path):
            raise FileNotFoundError(f"解压后未在 {extract_dir} 中找到 '{excel_filename}'")

        print(f"[Worker 真实] 4. 解压完成，路径: {excel_path}")

        # 8. 删除压缩包 (保持不变)
        try:
            os.remove(zip_file_path)
            print(f"[Worker 真实] 4. 已删除压缩包: {zip_file_path}")
        except OSError as e:
            print(f"[Worker 警告] 4. 删除压缩包 {zip_file_path} 失败: {e}")

        # 9. (!!修改!!) 返回动态的、正确的路径
        return excel_path

    def task_5_read_excel(self, excel_file_path):
        # 你的 "读取 Excel" (pandas) 代码放这里
        print(f"[Worker 真实] 5. 正在读取 {excel_file_path}...")

        # (!!注意!!)
        # 假设数据在第一个 sheet 中
        # 如果 Excel 有多个 sheet，你可能需要指定 sheet_name=...
        df = pd.read_excel(excel_file_path, engine='xlrd')

        print(f"[Worker 真实] 5. 读取完毕，返回 DataFrame。")
        return df

    def task_6_get_contact(self, order_id):
        # 你的 "根据订单号获取联系人" 的真实 requests.post 代码放这里
        print(f"[Worker 模拟] 6. 正在获取订单 {order_id} 的联系人...")
        url = "https://tts2.piao.qunar.com/order/queryOrderList"
        payload = {
            "displayId": order_id,
            "dateType": "canUseDate",
            "startDate": self.start_date,
            "endDate": self.end_date,
            "pageNo": 1,
            "perPageSize": 10
        }
        cookies = {"_s": self.session}
        response = requests.post(url, cookies=cookies, data=payload)
        response_json = response.json()
        contactUser = response_json.get("data")[0].get("orderList")[0].get("contactUser")
        return contactUser

    def task_7_save_to_excel(self, df, contacts, original_excel_path):
        # 你的 "回写 Excel" (pandas) 代码放这里
        print(f"[Worker 模拟] 7. 正在回写表格...")

        # 将获取到的联系人添加到 DataFrame
        df['ContactName'] = contacts

        # (模拟) 保存为新文件
        final_path = os.path.join(self.save_path, "orders_with_contacts.xlsx")
        df.to_excel(final_path, index=False, engine='openpyxl')

        # (可选) 删除中间文件
        # os.remove(original_excel_path)
        # os.remove(os.path.join(self.save_path, "exported_orders.zip"))

        return final_path


# --- 步骤 2: 修改主窗口类 (qunar_main) ---

class qunar_main(QWidget):
    # 定义一个信号，用于触发 worker 线程 (参数: session, start, end, save_path)
    trigger_export_flow = pyqtSignal(str, str, str, str)

    def __init__(self):
        super().__init__()
        self.ui = qunar_ui()
        self.ui.setupUi(self)

        self.init_dates()
        self.init_save_path()
        self.init_settings()

        # --- 步骤 3: 设置线程 ---
        self.setup_worker_thread()

        # --- 绑定按钮 ---
        self.ui.start_pushButton.clicked.connect(self.on_start_button_clicked)

        # --- 绑定日期 ---
        self.ui.startDateEdit.dateChanged.connect(self.on_start_date_changed)
        self.ui.endDateEdit.dateChanged.connect(self.on_end_date_changed)

        self.setWindowTitle("去哪儿导出工具")

    def setup_worker_thread(self):
        self.worker_thread = QThread()
        self.worker = ExportWorker()
        self.worker.moveToThread(self.worker_thread)

        # 连接信号与槽：

        # 1. (触发) 主线程 -> Worker (开始执行)
        self.trigger_export_flow.connect(self.worker.run_export_flow)

        # 2. (反馈) Worker -> 主线程 (更新UI)
        self.worker.signal_progress.connect(self.on_request_progress)
        self.worker.signal_finished.connect(self.on_request_finished)
        self.worker.signal_failure.connect(self.on_request_failure)

        # 3. (清理) 线程退出时自动清理
        self.worker_thread.finished.connect(self.worker_thread.deleteLater)

        # 启动线程
        self.worker_thread.start()
        print("[主线程] Worker 线程已启动并等待任务...")

    # -----------------------------------------------------------------
    # --- 按钮点击和 UI 响应槽函数 ---
    # -----------------------------------------------------------------

    def on_start_button_clicked(self):
        """
        "开始导出" 按钮被点击时，只负责发信号
        """
        # --- (新增的诊断 print) ---
        print("[主线程] '开始导出' 按钮被点击！")

        self.save_session()

        session = self.ui.qunar_session_lineEdit.text()
        start_date = self.ui.startDateEdit.date().toString("yyyy-MM-dd")
        end_date = self.ui.endDateEdit.date().toString("yyyy-MM-dd")
        save_path = self.ui.save_adress_lineEdit.text()

        # 验证输入
        if not session:
            QMessageBox.warning(self, "输入错误", "去哪儿 Session 不能为空，请输入。")
            return
        if not save_path:
            QMessageBox.warning(self, "输入错误", "保存路径不能为空，请选择。")
            return

        print(f"[主线程] 按钮点击！准备发射 trigger_export_flow 信号...")

        # 禁用按钮，防止重复点击
        self.ui.start_pushButton.setEnabled(False)
        self.ui.start_pushButton.setText("正在准备...")

        # (重要) 发射信号，触发 worker 线程开始工作
        self.trigger_export_flow.emit(session, start_date, end_date, save_path)

    def on_request_progress(self, message):
        """
        (在主线程执行) 收到 worker 的进度更新
        """
        print(f"[主线程] 收到 Progress: {message}")
        # (重要) 更新按钮文本来显示实时状态
        self.ui.start_pushButton.setText(message)

    def on_request_finished(self, message):
        """
        (在主线程执行) 收到 worker 的成功完成信号
        """
        print(f"[主线程] 收到 Finished 信号！")

        # 恢复按钮
        self.ui.start_pushButton.setEnabled(True)
        self.ui.start_pushButton.setText("开始导出")

        # 弹窗提示
        QMessageBox.information(self, "流程完成", message)

    def on_request_failure(self, error_message):
        """
        (在主线程执行) 收到 worker 的失败信号
        """
        print(f"[主线程] 收到 Failure 信号: {error_message}")

        # 恢复按钮
        self.ui.start_pushButton.setEnabled(True)
        self.ui.start_pushButton.setText("开始导出")

        # 弹窗提示
        QMessageBox.critical(self, "流程失败", error_message)

    # -----------------------------------------------------------------
    # --- 以下是上次已有的代码 (保持不变) ---
    # -----------------------------------------------------------------

    def closeEvent(self, event):
        print("窗口关闭，正在停止 Worker 线程...")
        if self.worker_thread.isRunning():
            self.worker_thread.quit()
            if not self.worker_thread.wait(5000):
                print("警告: 线程未能正常停止。")
                self.worker_thread.terminate()
        self.save_session()
        print("Session已保存，程序退出。")
        super().closeEvent(event)

    def init_dates(self):
        today = QDate.currentDate()
        start_of_month = QDate(today.year(), today.month(), 1)
        self.ui.startDateEdit.setDate(start_of_month)
        end_of_month = QDate(today.year(), today.month(), today.daysInMonth())
        self.ui.endDateEdit.setDate(end_of_month)

    def on_start_date_changed(self, new_start_date: QDate):
        if new_start_date.day() == 1:
            target_end_date = QDate(new_start_date.year(),
                                    new_start_date.month(),
                                    new_start_date.daysInMonth())
        else:
            target_end_date = new_start_date.addMonths(1)
        self.ui.endDateEdit.blockSignals(True)
        self.ui.endDateEdit.setDate(target_end_date)
        self.ui.endDateEdit.blockSignals(False)

    def on_end_date_changed(self, new_end_date: QDate):
        start_date = self.ui.startDateEdit.date()
        max_end_date = start_date.addMonths(1)
        if new_end_date > max_end_date:
            QMessageBox.warning(self, "日期范围错误",
                                f"选择的结束日期与开始日期 {start_date.toString('yyyy-MM-dd')} 相差超过一个月。\n"
                                f"已自动将结束日期调整为允许的最大日期 {max_end_date.toString('yyyy-MM-dd')}。")
            self.ui.endDateEdit.blockSignals(True)
            self.ui.endDateEdit.setDate(max_end_date)
            self.ui.endDateEdit.blockSignals(False)

    def init_save_path(self):
        self.ui.save_adress_lineEdit.setReadOnly(True)
        self.ui.save_adress_lineEdit.setPlaceholderText("点击选择保存文件夹")
        self.ui.save_adress_lineEdit.installEventFilter(self)

    def init_settings(self):
        self.settings = QSettings("MyCompany", "QunarApp")
        saved_session = self.settings.value("qunar_session", "")
        self.ui.qunar_session_lineEdit.setText(saved_session)

    def save_session(self):
        session_text = self.ui.qunar_session_lineEdit.text()
        self.settings.setValue("qunar_session", session_text)

    def open_folder_dialog(self):
        dir_path = QFileDialog.getExistingDirectory(self,
                                                    "选择保存文件夹",
                                                    QDir.homePath())
        if dir_path:
            self.ui.save_adress_lineEdit.setText(dir_path)

    def eventFilter(self, obj, event):
        if obj == self.ui.save_adress_lineEdit and event.type() == QEvent.MouseButtonPress:
            self.open_folder_dialog()
            return True
        return super().eventFilter(obj, event)

